package com.sinodata.bsm.cicp.util.telnet;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.swing.JOptionPane;

import org.apache.log4j.Logger;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UIKeyboardInteractive;
import com.jcraft.jsch.UserInfo;

/**
 * 利用 JSCH 实现的 SSH2.0.
 *
 * @author thomas.h.zhang
 */
public class JschSSH extends JSch implements Telnet {
    private final static Logger logger = Logger.getLogger(JschSSH.class);
    /**
     * 这是一个经验值, Shell 执行结果的每行长度不会大于256个字符. 但又不能分配得太小, 经实际测试 1024 为一个比较理想的尺寸.
     */
    private final byte[] buf = new byte[1024];
    private String shellPrompt;
    private String host;
    private int port;
    //    private String username;
    private String password;
    private InputStream in;
    private OutputStream out;
    private Session session;
    private Channel channel;

    /**
     * Set the prompt for the send() method.
     */

    @Override
    public void setPrompt(String prompt) {
        shellPrompt = prompt;
    }

    //标记此连接是否可用
    private boolean isValid = true;

    @Override
    public void setIsValid(boolean isValid) {
        this.isValid = isValid;
    }

    @Override
    public boolean getIsValid() {
        return this.isValid;
    }

    /**
     * Get the prompt.
     */

    @Override
    public String getPrompt() {
        return shellPrompt;
    }

    @Override
    public void connect(String host, int port) throws IOException {
        this.host = host;
        this.port = port;
    }

    @Override
    public void disconnect() throws IOException {
        if (in != null) {
            in.close();
            in = null;
        }
        if (out != null) {
            out.close();
            out = null;
        }
        if (channel != null) {
            channel.disconnect();
            channel = null;
        }
        if (session != null) {
            session.disconnect();
            session = null;
        }
    }

    @Override
    public void login(String user, String pwd, String prompt) throws IOException {
        //        username = user;
        password = pwd;
        this.setPrompt(prompt);

        try {

            session = getSession(user, host, port);
            session.setPassword(pwd);

            UserInfo ui = new MyUserInfo(password);
            session.setUserInfo(ui);

            // session.connect();
            // making a connection with timeout.
            session.connect(30 * 1000); // 30's  这个限制没起作用啊
            this.setSession(session);
            channel = session.openChannel("shell");

            // Enable agent-forwarding.
            // ((ChannelShell)channel).setAgentForwarding(true);

            in = channel.getInputStream();
            out = channel.getOutputStream();

            // ((ChannelShell) channel).setPtyType("vt102");
            // java.util.Hashtable env = new java.util.Hashtable();
            // env.put("LANG", "ja_JP.eucJP");
            // ((ChannelShell) channel).setEnv(env);

            //            channel.connect();
            channel.connect(30 * 1000); // 10'm

            // this.recvResp();
        } catch (JSchException e) {
            if (in != null) {
                in.close();
                in = null;
            }
            if (out != null) {
                out.close();
                out = null;
            }
            if (channel != null) {
                channel.disconnect();
                channel = null;
            }
            logger.error("Failed to execute command!", e);
            throw new IOException("Failed to execute command!");

        }
    }

    //    public void openChannel(Telnet telnet, Session session) {
    //        channel = session.openChannel("shell");
    //
    //    }

    @Override
    public void write(byte[] b) throws IOException {
        out.write(b);
    }

    @Override
    public int read(byte[] b) throws IOException {
        return in.read(b);
    }

    @Override
    public void sendCmd(String cmd) throws IOException {
        // send command to Telnet server
        out.write(cmd.getBytes());

        // write CRLF indicate end of command
        out.write("\n".getBytes());
        out.flush();
    }

    private final static byte start = 0x1B;
    private final static byte end = 0x6D;

    /**
     * 过滤掉 "[m" "[00m" "[01;34m" 等类似的乱字符
     *
     * @param b
     * @return
     */
    private String filterWorngChar(byte[] b, int n) {
        //TODO 去掉特殊字符的替代方法
        // return ByteUtil.trim(b, n, start, end);
        return null;
    }

    // 转换执行结果   这里是不是要换成StringBuffer

    @Override
    public String recvResp() throws IOException {
        String rs = "";
        while (true) {
            int n = in.read(buf);
            if (n < 0) {
                logger.error("Failed to recvice data from SSH2.0 server: " + host);
                throw new IOException("Failed to recvice data from SSH2.0 server: " + host);
            }

            String s = filterWorngChar(buf, n);
            // System.err.print(s);
            // String s = new String(buf, 0, n);
            rs += s;
            // if (s.indexOf(this.getPrompt()) >= 0)
            if (isTermianl(rs)) {
                return rs;
            }
        }
    }

    // 判断结尾处是否为提示符.
    // 结束符必须在末尾.
    private boolean isTermianl(String buf) {
        int promptLen = shellPrompt.length();
        int bufLen = buf.length();
        if (promptLen > bufLen) {
            return false;
        }

        String last = buf.substring(bufLen - promptLen, bufLen);
        if (shellPrompt.equals(last)) {
            return true;
        } else {
            // 末尾包含一个空格的提示符
            String spacePrompt = shellPrompt + " ";
            promptLen = spacePrompt.length();
            if (promptLen > bufLen) {
                return false;
            }
            last = buf.substring(bufLen - promptLen, bufLen);
            if (spacePrompt.equals(last)) {
                return true;
            }
        }

        return false;
    }

    @Override
    public void cancel() throws IOException {
        sendCmd("\003"); // ^C
        recvResp();
    }

    @Override
    public void runLogin(String user, String pwd, String prompt) throws IOException {
        LoginTask task = new LoginTask(this, user, pwd, prompt);
        task.start();

        int count = 0;
        // wait until last task is complete
        while (!task.isComplete() && task.isActive()) {
            try {
                Thread.sleep(10);
                count++;
                if (count > TIMEOUT_SECOND * 50) {
                    Thread.interrupted();
                    task.stop();
                    logger.error("Login timeout: " + 17 + "s ");
                    throw new TelnetTaskTimeoutException("Login timeout: " + 17 + "s ");
                }
            } catch (InterruptedException e) {
                logger.error(e);
            }
        }
        if (task.isError()) {
            logger.error("Login exception: " + task.getErrorMsg());
            throw new TelnetException("Login exception: " + task.getErrorMsg());
        }
    }

    @Override
    public String runTask(String shell) throws TelnetException {
        TelnetTask task = new TelnetTask(this, shell);

        task.start();
        int count = 0;
        // wait until last task is complete
        while (!task.isComplete() && task.isActive()) {
            try {
                Thread.sleep(10);
                count++;
                if (count >= TIMEOUT_SECOND * 50) {
                    //                    try {
                    //                        cancel();
                    //                    } catch (IOException e) {
                    //                    }
                    try {
                        sendCmd("\003");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    Thread.interrupted();
                    task.stop();
                    logger.error("Execute command:[" + task.getCmd() + "] timeout:" + TIMEOUT_SECOND + "s");
                    throw new TelnetTaskTimeoutException("Execute the command:[" + task.getCmd() + "] timeout:" + TIMEOUT_SECOND + "s");
                }
            } catch (InterruptedException e) {
                logger.error("Exception occurred while executing the command!", e);
                throw new TelnetException("Exception occurred while executing the command!", e);
            }
        }
        return task.getShellOut();
    }

    public Session getSession() {
        return session;
    }

    public void setSession(Session session) {
        this.session = session;
    }

    public InputStream getIn() {
        return in;
    }

    public void setIn(InputStream in) {
        this.in = in;
    }

    public OutputStream getOut() {
        return out;
    }

    public void setOut(OutputStream out) {
        this.out = out;
    }

    public Channel getChannel() {
        return channel;
    }

    public void setChannel(Channel channel) {
        this.channel = channel;
    }

    public static void main(String[] args) throws Exception {
        Telnet ssh = new JschSSH();

        String host = "172.168.2.248";
        // String host = "60.28.249.142";
        // String host = "60.28.249.144";
        int port = 22;
        String username = "root";
        String password = "zk_1234";
        // String prompt = "$";
        String prompt = "#";
        //                        String cmd = "free";
        String recv;

        ssh.connect(host, port);

        // ssh.login(username, password, prompt);
        // 接收登录提示
        // recv = ssh.recvResp();
        ssh.runLogin(username, password, prompt);

    }

    /*    private static void writeToFile(String filename, String s) throws FileNotFoundException, IOException {
            File file = new File(filename);
            FileOutputStream fos = new FileOutputStream(file);
            OutputStreamWriter osr = new OutputStreamWriter(fos);
            BufferedWriter bw = new BufferedWriter(osr);

            bw.write(s);
            bw.flush();
            bw.close();
            osr.close();
            fos.close();
        }*/

    public class MyUserInfo implements UserInfo, UIKeyboardInteractive {
        private final String password;

        public MyUserInfo(String password) {
            this.password = password;
        }

        @Override
        public String getPassword() {
            return this.password;
        }

        @Override
        public boolean promptYesNo(String str) {
            return true;
        }

        @Override
        public String getPassphrase() {
            return null;
        }

        @Override
        public boolean promptPassphrase(String message) {
            return true;
        }

        @Override
        public boolean promptPassword(String message) {
            return true;
        }

        @Override
        public void showMessage(String message) {
            JOptionPane.showMessageDialog(null, message);
        }

        @Override
        public String[] promptKeyboardInteractive(String destination, String name, String instruction, String[] prompt, boolean[] echo) {
            return new String[] { this.password };
        }
    }
}
